package com.metel.mservice.common.jaxrs.interceptor;

import java.io.IOException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.metel.mservice.common.constants.HttpStatus;
import com.metel.mservice.common.context.OpenAPIContextManager;
import com.metel.mservice.common.exception.CommonServiceException;
import com.metel.mservice.common.exception.InternalServerErrorException;
import com.metel.mservice.common.utils.CRMLogUtil;
import com.metel.mservice.common.utils.CRMLogUtil.LOG_TYPE;
import com.metel.mservice.common.utils.CRMLogUtil.STATUS;

/**
 * FaultInterceptor
 * 
 * @author sr80.choi
 */
public class FaultInterceptor extends AbstractPhaseInterceptor<Message> {

	private final static Logger logger = LoggerFactory.getLogger(FaultInterceptor.class);

	public FaultInterceptor() {
		super(Phase.PRE_STREAM);
	}

	public void handleMessage(Message message) throws Fault {

		Exception exception = message.getContent(Exception.class);
		if(exception != null){
			if(displayDebugInfo(exception) || OpenAPIContextManager.isLocalEnvironment()) {
				// 개발환경인 경우에 한해서, trace 코드 출력..
				exception.printStackTrace();
			} else {
//				logger.warn("Fault - {}", exception.getMessage());
//				logger.warn("Caused by: {}", getCausedString(exception));
				logger.warn(CRMLogUtil.getCrmLog(LOG_TYPE.ING, "handleMessage", STATUS.END_API, "Fault - {}"), exception.getMessage());
				logger.warn(CRMLogUtil.getCrmLog(LOG_TYPE.ING, "handleMessage", STATUS.END_API, "Caused by: {}"), getCausedString(exception));
			}
			
			HttpServletResponse response = (HttpServletResponse) message.getExchange().getInMessage()
					.get(AbstractHTTPDestination.HTTP_RESPONSE);
			handleException(exception, response);			
		}else{
			if(	OpenAPIContextManager.isLocalEnvironment()) {
				Thread.dumpStack();
			}
		}
		// abort chain
		message.getInterceptorChain().abort();
	}

	private boolean displayDebugInfo(Exception exception) {
		boolean displayExceptionInfo = false;
		
		CommonServiceException commonServiceException = findException(exception);
		
		if(commonServiceException != null) {
			Integer httpStatus = commonServiceException.getHttpResponseCode();
			String message = commonServiceException.getMessage();
			if(message == null) message = "";
//			logger.info("Response-Code: "+httpStatus+"\nPayload: "+message);
			logger.info(CRMLogUtil.getCrmLog(LOG_TYPE.ING, "handleMessage", STATUS.END_API, "Response-Code: {} \nPayload: {}"), httpStatus, message);
			
			if ( httpStatus.equals(HttpStatus.INTERNAL_SERVER_ERROR.getStatusCode()) ) {
				displayExceptionInfo = true;
			}
		}

		return displayExceptionInfo;
	}
	
	private void handleException(Exception exception, HttpServletResponse response) {
		Integer responseCode = null;
		String message = null;

		// find CommonServiceException
		CommonServiceException commonException = findException(exception);
		if(commonException == null) {
			// not CommonServiceException
			// default : InternalServerErrorException
			commonException = new InternalServerErrorException();
		}

		// get response code
		responseCode = commonException.getHttpResponseCode();
		// get error message
		message = commonException.getErrorMessage();

		writeResponse(response, responseCode, message);
	}

	private CommonServiceException findException(Throwable t) {
		CommonServiceException e = null;
		
		if(t != null) {
			if(t instanceof CommonServiceException) {
				return (CommonServiceException) t;
			}
			
			Throwable innerCause = t.getCause();
			if(innerCause != null) {
				e = findException(innerCause);
			}
		}
		
		return e;
	}
	
	private void writeResponse(HttpServletResponse response, Integer responseCode, String message) {
		// set httpResponseCode
		response.setStatus(responseCode);

		// write responseBody
		ServletOutputStream outputStream = null;
		try {
			outputStream = response.getOutputStream();

			if (responseCode != 204 && message != null) {
				int last = message.lastIndexOf("]");
				int first = message.lastIndexOf("[");
				
				String errorCode  = message.substring(first+1, last);
				String errorMsg = message.substring(last+1, message.length());
				
				StringBuilder sb = new StringBuilder();
//				sb.append(message);
				sb.append("{\"error_code\":\"")
				  .append(errorCode.trim())
				  .append("\",\"error_msg\":\"")
				  .append(errorMsg.trim())
				  .append("\"}");

				outputStream.write(sb.toString().getBytes("UTF-8"));
			}
		} catch (Exception e) {
			// e.printStackTrace();
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					outputStream = null;
				}
			}
		}
	}

	private String getCausedString(Exception exception) {
		Throwable t = exception.getCause();

		String causedString = null;
		try {
			if (t != null) {
				StackTraceElement stackTraceElement = t.getStackTrace()[0];
				causedString = stackTraceElement.toString();
			} else {
				StackTraceElement stackTraceElement = exception.getStackTrace()[0];
				causedString = stackTraceElement.toString();
			}
		} catch (Exception e) {
			exception.printStackTrace();
		} finally {
			if (causedString == null) {
				causedString = "Unknown";
			}
		}

		return causedString;
	}
	/*
	@SuppressWarnings("unused")
	@Deprecated
	private void writeLog(Integer responseCode, String uid, String imei, String errorMessage) {
		String message;
		if (errorMessage == null) {
			message = ":" + errorMessage;
		} else {
			message = "";
		}

		if (responseCode == 200) {
			if (logger.isDebugEnabled()) {
				logger.debug("[{}/{}] Status {}{}", new Object[] { uid, imei, responseCode, message });
			} else {
				logger.info("[{}] Status {}", new Object[] { uid, responseCode });
			}
		} else if (responseCode == 500) {
			logger.error("[{}/{}] Status {}{}", new Object[] { uid, imei, responseCode, message });
		} else {
			logger.info("[{}] Status {}{}", new Object[] { uid, responseCode, message });
		}
	}
	*/
}